package com.ftwj.demo.global.shiro;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.crazycake.shiro.serializer.ObjectSerializer;
import org.crazycake.shiro.serializer.StringSerializer;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ftwj.demo.utils.logs.LoggerUtil;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

@Configuration
public class CustomShiroConfig {
	
	@Value("${spring.redis.host}")
	private String host;
	@Value("${spring.redis.port}")
	private String port;
	@Value("${spring.redis.password}")
	private String password;
	@Value("${spring.redis.shiro}")
	private int base;
    
    @Autowired
    private CustomShiroRealm realm;

    /**
     * 
     * @Title: realm
     * @Description: 自定义Realm
     * @author: WangShengLi
     * @return
     * @return: Realm
     */
    @Bean
    public Realm realm() {
        //TextConfigurationRealm realm = new TextConfigurationRealm();
        //JdbcRealm realm = new JdbcRealm();
        realm.setCachingEnabled(true);
        realm.setCredentialsMatcher(hashedCredentialsMatcher());
        return realm;
    }
    
    /**
     * 
     * @Title: securityManager
     * @Description: shiro核心安全管理器
     * @author: WangShengLi
     * @param ehcacheManager
     * @return
     * @return: DefaultWebSecurityManager
     */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setSessionManager(sessionManager());
        securityManager.setCacheManager(redisCacheManager());
        //securityManager.setCacheManager(cacheManager());
        securityManager.setRealm(realm());
        return securityManager;
    }
    
    /**
     * 	配置加密方式
     * @return
     */
    
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        //盐
        //HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //自定义密码比较器附带账户锁定功能
    	RedisCacheManager passwordRetryCacheManager = redisCacheManager();
    	passwordRetryCacheManager.setExpire(60*5);
        RetryLimitHashedCredentialsMatcher hashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(passwordRetryCacheManager);
        //配置散列算法，，使用MD5加密算法
        hashedCredentialsMatcher.setHashAlgorithmName(Md5Hash.ALGORITHM_NAME);
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        //设置散列次数
        hashedCredentialsMatcher.setHashIterations(64);
        return hashedCredentialsMatcher;
    }
    
	
    @Bean
    public SessionManager sessionManager(){
    	DefaultWebSessionManager shiroSessionManager = new DefaultWebSessionManager();
        //这里可以不设置。Shiro有默认的session管理。如果缓存为Redis则需改用Redis的管理
        shiroSessionManager.setSessionDAO(new EnterpriseCacheSessionDAO());
        shiroSessionManager.setSessionDAO(redisShiroSessionDAO());
        //SimpleCookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
        SimpleCookie cookie = new SimpleCookie("LZFTWJXXCYYXGS");
        cookie.setHttpOnly(false);
        cookie.setMaxAge(60*60*24);
        shiroSessionManager.setSessionIdCookie(cookie);
        shiroSessionManager.setGlobalSessionTimeout(30*60*1000L);
        return shiroSessionManager;
    }
    
    @Bean
    public RememberMeManager rememberMeManager(){
    	CookieRememberMeManager rememberMeManger = new CookieRememberMeManager();
    	SimpleCookie cookie = new SimpleCookie("FTWJREMEMBER");
    	cookie.setHttpOnly(false);
        cookie.setMaxAge(60*60*24*7);
        rememberMeManger.setCookie(cookie);
        return rememberMeManger;
    }

	/**
     * 
     * @Title: shiroDialect
     * @Description: 配置Thymleaf中Shiro标签的使用
     * @author: WangShengLi
     * @return
     * @return: ShiroDialect
     */
    
    
    @Bean
    public ShiroDialect shiroDialect() {
    	LoggerUtil.info("配置Thymleaf方言ShiroDialect：SUCCESS");
        return new ShiroDialect();
    }
    
    /**
     * 
     * @Title: getDefaultAdvisorAutoProxyCreator
     * @Description: setUsePrefix(false)用于解决一个奇怪的bug： 
     * 				  在引入spring aop的情况下，在@Controller注解的类的方法中加入@RequiresRole注解，
     * 				  会导致该方法无法映射请求，导致返回404。加入这项配置能解决这个bug。
     * @author: WangShengLi
     * @return
     * @return: DefaultAdvisorAutoProxyCreator
     */
    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
    	DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
    	//creator.setUsePrefix(true);
    	creator.setProxyTargetClass(true);
    	return creator;
	}
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    	AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;

    }
    
    /**
     * 	配置shiroFilter过滤器,spring-boot-start-shiro中已做默认配置
     * @return
     */
    /*
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //Shiro的核心安全接口,这个属性是必须的
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        //shiroFilterFactoryBean.setLoginUrl("");//身份认证失败，则跳转到登录页面的配置 没有登录的用户请求需要登录的页面时自动跳转到登录页面，不是必须的属性。
        //shiroFilterFactoryBean.setSuccessUrl("");//登录成功默认跳转页面，不配置则跳转至"/"。如果登陆前点击的一个需要登录的页面，则在登录自动跳转到那个需要登录的页面，不跳转到此。
        //shiroFilterFactoryBean.setUnauthorizedUrl("");//没有权限默认跳转的页面
        //shiroFilterFactoryBean.setFilterChainDefinitions("");//filterChainDefinitions的配置顺序为自上而下,以最上面的为准
        //当运行一个Web应用程序时,Shiro将会创建一些有用的默认Filter实例,并自动地在[main]项中将它们置为可用自动地可用的默认的Filter实例是被DefaultFilter枚举类定义的,枚举的名称字段就是可供配置的名称

        //在不同的服务中配置不同的PathMatchingFilter实现，完成不同的过滤规则
        Map<String, Filter> filters=new LinkedHashMap<String, Filter>();
        filters.put("filter", new CustomLoginFilter());
        shiroFilterFactoryBean.setFilters(filters);
        //权限配置
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/**", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    */
    
	/**
	 * 
	 * @Title: shiroFilterChainDefinition
	 * @Description: 过滤规则，如果使用了自定义过滤器之后则不再使用,与shiroFilterFactoryBean()选一
	 * @author: WangShengLi
	 * @return
	 * @return: ShiroFilterChainDefinition
	 */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        chainDefinition.addPathDefinition("/**", "anon");
        return chainDefinition;
    }
    
    /**
     * 
     * @Title: getEhCacheManager
     * @Description: Shiro缓存配置,分别为Shiro内置，Ehcache，Redis
     * @author: WangShengLi
     * @return
     * @return: EhCacheManager
     */
    
    /**
     * 
     * <p>Title: redisManager</p>  
     * <p>Description: MemoryCache缓存Shiro信息</p>  
     * @return 
     * @author WangsLi
     * @date 2020年3月7日  
     * @version 1.0
     */
    /*
    @Bean
    protected CacheManager cacheManager() {
        return new MemoryConstrainedCacheManager();
    }
    */
    
    /**
     * 
     * <p>Title: redisManager</p>  
     * <p>Description: EhCache缓存Shiro信息</p>  
     * @return 
     * @author WangsLi
     * @date 2020年3月7日  
     * @version 1.0
     */
    /*
	@Bean
	protected EhCacheManager getEhCacheManager(CacheManager cache) {
		EhCacheManager ehcacheManager = new EhCacheManager();
		ehcacheManager.setCacheManager(cache);
		//ehcacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
		return ehcacheManager;
	}
     */
    
    /**
     * 
     * <p>Title: redisManager</p>  
     * <p>Description: Redis缓存Shiro信息</p>  
     * @return 
     * @author WangsLi
     * @date 2020年3月7日  
     * @version 1.0
     */
    /*
     * 	使用Redis缓存时必须重写RedisSessionDAO，RedisCacheManager，RedisManager
     */
    
    @Bean
    public RedisManager redisManager() {
    	RedisManager redisManager = new RedisManager();
    	//redisManager.setJedisPoolConfig(jedisPoolConfig);
    	redisManager.setHost(host+":"+port);
    	redisManager.setPassword(password);
    	redisManager.setDatabase(base);
    	//若想要换jedis为lettuce连接池，则需要手动实现RedisManager和RedisCacheManager
    	return redisManager;
    }
    
    @Bean
    public RedisSessionDAO redisShiroSessionDAO() {
        RedisSessionDAO redisShiroSessionDAO = new RedisSessionDAO();
        redisShiroSessionDAO.setRedisManager(redisManager());
        redisShiroSessionDAO.setKeyPrefix("shiro:session:");
        redisShiroSessionDAO.setExpire(60*30);
        return redisShiroSessionDAO;
    }
	
    @Bean(name="shiroCahceManager")
    public RedisCacheManager redisCacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setExpire(60*30);
        //指定存入Redis的主键前缀
        redisCacheManager.setKeyPrefix("shiro:");
        redisCacheManager.setPrincipalIdFieldName("phone");
        redisCacheManager.setKeySerializer(new StringSerializer());
        redisCacheManager.setValueSerializer(new ObjectSerializer());
        return redisCacheManager;
    }
    
    /*
    @Bean(name="passwordRetryCacheManager")
    public RedisCacheManager passwordRetryCacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setExpire(60*5);
        //指定存入Redis的主键前缀
        redisCacheManager.setKeyPrefix("password:retry:");
        redisCacheManager.setPrincipalIdFieldName("phone");
        redisCacheManager.setKeySerializer(new StringSerializer());
        redisCacheManager.setValueSerializer(new ObjectSerializer());
        return redisCacheManager;
    }
    */
    
}
